package com.tarry.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketClient {
    private final String serverHost;
    private final int port;
    private Selector selector;
    private SelectionKey selectionKey;
    private ExecutorService executorService = Executors.newSingleThreadExecutor();
    private SocketChannel client;

    public SocketClient(String serverHost, int port) {
        this.serverHost = serverHost;
        this.port = port;
    }

    public void connect() throws IOException {
        final SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        this.selector = Selector.open();
        this.selectionKey = socketChannel.register(this.selector, 0);
        final boolean isConnected = socketChannel.connect(new InetSocketAddress(this.serverHost, this.port));
        if (!isConnected) {
            // 当连接不成功时，设置对连接监听
            this.selectionKey.interestOps(SelectionKey.OP_CONNECT);
        }
        selector.select();
        final Set<SelectionKey> selectionKeys = selector.selectedKeys();
        final Iterator<SelectionKey> iterator = selectionKeys.iterator();
       // if (iterator.hasNext()) {
            this.selectionKey = iterator.next();
            iterator.remove();
       // }
        final int readyOps = this.selectionKey.readyOps();
        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
            this.client = (SocketChannel) this.selectionKey.channel();
        }
        this.executorService.execute(this::handleEvent);
    }

    private void handleEvent() {
        try {
            while (true) {
                this.selector.select();
                final Set<SelectionKey> selectionKeys = this.selector.selectedKeys();
                final Iterator<SelectionKey> iterator = selectionKeys.iterator();
                SocketChannel client;
                while (iterator.hasNext()) {
                    final SelectionKey selectionKey = iterator.next();
                    iterator.remove();
                    final int readyOps = selectionKey.readyOps();
                    if ((readyOps & SelectionKey.OP_CONNECT) != 0 ){
                        System.out.println("未连接～～～～");
                    }else if ((readyOps & SelectionKey.OP_READ) != 0) {
                        // 读取事件
                        client = (SocketChannel) selectionKey.channel();
                        final ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                        readBuffer.clear();
                        final int count = this.client.read(readBuffer);
                        if ( count > 0 ) {
                            final String msg = new String(readBuffer.array(), 0, count);
                            System.out.println("receive msg from server:" + msg);
                        }
                    }else {
                        System.out.println("其他事件～～～");
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private void sendMsg() throws IOException {
        // 等待客户端Socket完成与服务端对连接
        if ( !client.finishConnect()) {
            throw new Error();
        }
        final ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
        sendBuffer.clear();
        String msg = new Date() + " hello server.";
        sendBuffer.put(msg.getBytes(StandardCharsets.UTF_8));
        sendBuffer.flip();
        this.client.write(sendBuffer);
        System.out.println("client send msg: " + msg);
        if (Objects.nonNull(this.selectionKey)) {
            // 设置读取事件监听
            this.selectionKey.interestOps(SelectionKey.OP_READ);
        }
    }

    public static void main(String[] args) throws IOException {
        final SocketClient client = new SocketClient("127.0.0.1", 3333);
        client.connect();
        client.sendMsg();
    }
}
